#include "bigm.h"

extern void deleteEntry();

void A();
void B();
void b();
void copyFile();
void showProgressBar(int progress);

// 备份程序入口
void bf() {
    int n;
    
    printf("\n---------------------文件备份---------------------\n\n");
    
    do {
        printf("请选择想要备份的模式：\n");
        printf("1. 备份单份\n");
        printf("2. 备份多份\n");
        printf("3. 退出\n");
        printf("请输入1、2或3进行选择：\n");
        scanf("%d", &n);
        printf("\n---------------------------------------------------\n");
        
        switch(n) {
            case 1:
                A();
            	break;
            case 2:
            	B();
            	break;
            case 3:
            	printf("文件备份程序退出！\n");
		return ;
            default:
            	printf("无效选择，请重试！\n\n");
            	continue;
        }
        
        printf("\n");
    }while (1);
}

typedef struct {
    char srcPath[PATH_MAX];
    char destPath[PATH_MAX];
} CopyTask;
           
// 备份单份输入提示
void A() {
    char file1[100];
    char file2[200];
    
    printf("请输入想要备份的文件：");
    scanf("%s", file1);
    printf("请输入想要备份到的文件名（可以不存在）：");
    scanf("%s", file2);
    
    const char* src = file1;
    const char* dest = file2;

    // 使用提供的源目录和目标目录调用备份函数
    b(src, dest);
    printf("备份任务完成！[\n");
}

// 备份多份输入提示
void B() {
    // 声明任务列表数组
    char tasks[MAX_TASKS][2][MAX_PATH_LENGTH];
    // 记录任务数
    int numTasks = 0;

    // 循环获取任务信息，直到达到最大任务数或输入结束标志
    while (numTasks < MAX_TASKS) {
        // 输入源目录路径
        printf("请输入想要备份的文件（结束请输入'q'）：");
        if (scanf("%s", tasks[numTasks][0]) != 1 || tasks[numTasks][0][0] == 'q') {
            break;
        }

        // 输入目标目录路径
        printf("请输入想要备份到的文件名（可以不存在）：");
        scanf("%s", tasks[numTasks][1]);
        printf("\n");

        // 增加任务数
        numTasks++;
    }

    // 多进程备份任务
    int processesCount = 0; // 当前进行的进程数
    for (int i = 0; i < numTasks; i++) {
        pid_t pid = fork();
        if (pid < 0) {
            printf("创建子进程时发生错误!\n");
        } else if (pid == 0) {
            // 子进程中进行备份任务
            b(tasks[i][0], tasks[i][1]);
            exit(0); // 子进程完成任务后退出
        } else {
            processesCount++;
            if (processesCount >= MAX_PROCESSES) {
                // 达到最大进程数，等待其中一个进程结束
                wait(NULL);
                processesCount--;
            }
        }
    }

    // 等待所有进程结束
    while (processesCount > 0) {
        wait(NULL);
        processesCount--;
    }

    printf("备份任务完成！[\n");
}

// 备份的函数
void b(const char* src, const char* dest) {
	DIR* dir1 = opendir(dest);
	if (dir1 > 0){
		deleteEntry(dest);
	}
	
	closedir(dir1);
	
    DIR* dir = opendir(src);
    if (dir == NULL) {
        printf("打开源目录时发生错误!\n");
        return;
    }

    // 如果目标目录不存在，则创建目标目录
    struct stat destStat;
    if (lstat(dest, &destStat) == -1) {
        if (mkdir(dest, 0777) == -1) {
            printf("创建目录时发生错误!\n");
            closedir(dir);
            return;
        }
    }

    struct dirent* entry;
    int fileCount = 0;
    int threadCount = 0;
    pthread_t threads[MAX_THREADS];

    while ((entry = readdir(dir)) != NULL) {
        // 忽略当前目录"."和上级目录".."
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char srcPath[PATH_MAX];
        snprintf(srcPath, sizeof(srcPath), "%s/%s", src, entry->d_name);

        struct stat srcStat;
        if (lstat(srcPath, &srcStat) == -1) {
            printf("获取源文件信息时发生错误!\n");
            continue;
        }

        char destPath[PATH_MAX];
        snprintf(destPath, sizeof(destPath), "%s/%s", dest, entry->d_name);

        if (S_ISDIR(srcStat.st_mode)) {
            // 如果当前项是目录，则递归备份其内容
            b(srcPath, destPath);
        } else {
            if (srcStat.st_size > FILE_COPY_THRESHOLD) {
             	// 当单个文件大小大于FILE_COPY_THRESHOLD时，开启多线程备份
            	CopyTask* task = (CopyTask*)malloc(sizeof(CopyTask));
            	strncpy(task->srcPath, srcPath, sizeof(task->srcPath));
            	strncpy(task->destPath, destPath, sizeof(task->destPath));
            	pthread_create(&threads[threadCount], NULL, (void*)copyFile, (void*)task);
            	threadCount++;
            } else {
                // 使用单个线程备份单个文件
                copyFile(srcPath, destPath);
            }
        }
    }

    closedir(dir);

    // 等待多线程备份完成
    for (int i = 0; i < threadCount; i++) {
        pthread_join(threads[i], NULL);
    }
}

// 用于备份单个文件的函数
void copyFile(const char* srcPath, const char* destPath) {
    struct stat st;
    if (stat(srcPath, &st) == 0 && S_ISREG(st.st_mode)) {
        FILE* srcFile = fopen(srcPath, "rb");
        FILE* destFile = fopen(destPath, "wb");

        if (srcFile == NULL || destFile == NULL) {
            printf("打开源文件时发生错误!\n");
            return;
        }
        
        char buffer[4096];
	    size_t bytesRead;
	    long long totalSize = 0;
	    long long copiedSize = 0;

	    fseek(srcFile, 0, SEEK_END);
	    totalSize = ftell(srcFile);
	    fseek(srcFile, 0, SEEK_SET);
	
	    while ((bytesRead = fread(buffer, 1, sizeof(buffer), srcFile)) > 0) {
	        fwrite(buffer, 1, bytesRead, destFile);
	        copiedSize += bytesRead;
	
	        int progress = (int) ((copiedSize * 100) / totalSize);
	        
	        showProgressBar(progress);
	        fflush(stdout);
	    }
        fclose(srcFile);
        fclose(destFile);
    }
}

// 用于实现进度条的函数
void showProgressBar(int progress) {
    if (progress < 0) {
        progress = 0;
    } else if (progress > 100) {
        progress = 100;
    }

    int barWidth = 50;
    int block = (int)((progress / 100.0) * barWidth);
    
    for (int i = 0; i < barWidth; i++) {
        if (i < block) {
            printf("=");
        } else {
            printf(" ");
        }
    }
    printf("] %d%%\r", progress);
    fflush(stdout);
}
